ホームに戻る
出典 :
第4回 WPFの「リソース、スタイル、テンプレート」を習得しよう:連載:WPF入門 - @IT C#のWPFでよく使うXAMLの定義を別ファイルにする - Ararami Studio
関連 :
スタイル テンプレート ユーザーコントロール Tagプロパティ
目次 :

リソースとは

複数のコントロール(UI要素)でひとつのオブジェクトを共有するための仕組み。
例えば背景色を指定するためのブラシをリソースとして作成しておき、複数の Button で共有するといった使い方ができる。
スタイルの指定に用いられるのが一般的である。

リソースの定義

リソースは( System.Windows.FrameworkElement 型を継承する) Window 等の要素の、Resources プロパティ内に定義する。
この Resources プロパティは ResourceDictionary 型の辞書(連想配列)で、キー、値のいずれもが object 型となっている。
要素の定義(追加)の際は、キーとして使用される x:Key 属性の指定が必須である
(XAML)
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="100" Width="200"> <!-- リソース(SolidColorBrush)の定義 --> <!-- キー(x:Key)に MyBrush を指定 --> <Window.Resources> <SolidColorBrush x:Key="MyBrush" Color="Blue"/> </Window.Resources> <StackPanel> <!-- Button にリソースを適用 --> <Button Content="Button 1" Background="{StaticResource ResourceKey=MyBrush}"/> <Button Content="Button 2" Background="{StaticResource ResourceKey=MyBrush}"/> </StackPanel> </Window>
ここで "ResourceKey=" は省略可能。以下も同様。
(表示)

ふたつの Button に背景色が設定されている。このとき、生成される Brush はひとつである点に注意。

通常、ひとつのリソースではひとつのオブジェクトが作成され、複数のコントロールで共有される。
コントロールごとにオブジェクトを作成するよりもオーバーヘッドが削減できる。
尚、コントロールごとに異なるオブジェクトを割り当てることも可能。その場合は x:Shared プロパティを False とする。
: <!-- リソース(SolidColorBrush)の定義 --> <!-- (コントロールごとにオブジェクトを作成する) --> <Window.Resources> <SolidColorBrush x:Shared="False" x:Key="MyBrush" Color="Blue"/> </Window.Resources> :

リソースの参照

リソースの参照時は、StaticResource または DynamicResource マークアップ拡張を用いる
リソースの検索は、内側(子孫)から外側(祖先)に向かって行われる。
このため、同名のキーを持つリソース・ディクショナリが存在する場合は、より内側にあるリソースが優先される
<Window x:Class=" WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!-- (Window) Brush1Brush2 の定義 --> <Window.Resources> <SolidColorBrush x:Key="Brush1" Color="Red" /> <SolidColorBrush x:Key="Brush2" Color="Red" /> </Window.Resources> <StackPanel> <!-- (StackPanel) Brush2 の定義 --> <StackPanel.Resources> <SolidColorBrush x:Key="Brush2" Color="Blue" /> </StackPanel.Resources> <Button Content="ボタン1" Background="{StaticResource ResourceKey=Brush1}"/> <!-- より内側にある StackPanel の Brush2 を参照する --> <Button Content="ボタン2" Background="{StaticResource ResourceKey=Brush2}"/> </StackPanel> </Window>
ここでは Window と StackPanel にそれぞれ "Brush2" が定義されているが、「ボタン2」にはより内側にある StackPanel の "Brush2" が適用される。
参照には StaticResource マークアップ拡張を用いている。

システム・リソース

オペレーティングシステムで保持している設定(色、フォントなど)をリソース(システム・リソース)として用いることができる。
システム・リソースを利用するには以下のクラスを使用する。
(いずれも名前空間は System.Windows ) 以下の例では、デスクトップの背景色を使用している。
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Width="200" Height="200"> <!-- デスクトップの背景色を Grid の背景色として使用 --> <Grid Background="{DynamicResource {x:Static SystemColors.DesktopBrushKey}}"> : </Grid> </Window>
ここではシステム・リソースの参照に DynamicResource マークアップ拡張を用いている。
これにより、アプリケーション外でデスクトップの背景色が変更されると、即座にその変更がアプリケーションに反映される。
(デスクトップの背景色が変わると、即座に Grid の背景色が変わる。)

外部リソース

リソースは ####.Resources タグ中に直接定義する以外に、別途用意したXAMLファイル中に定義したリソースをインポートすることが可能である。
(リソースファイルを参照する際は相対パスを使用できる。)
外部リソースのルート要素は ResourceDictionary とし、配下にリソースの各要素を定義する。
尚、Visual Studioで外部リソースを新規作成する場合は、「リソース ディクショナリ(WPF)」を選択する。
(XAMLファイルが新規に作成される。)
外部リソース(Styles.xaml)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Style TargetType="Button"> <Setter Property="Background" Value="LightBlue" /> </Style> </ResourceDictionary>
参照側では ####.Resources タグ配下に ResourceDictionary 要素を記述、Source 属性により参照先を指定する。
(ウィンドウ)
<Window x:Class=" WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!-- 外部リソース(Style.xaml)をインポート --> <Window.Resources> <ResourceDictionary Source="Styles.xaml"/> </Window.Resources> <StackPanel> <!-- インポートしたスタイルが Button に適用される --> <Button Content="ボタン1"/> </StackPanel> </Window>

ResourceDictionary の統合( MergedDictionaries )

ResourceDictionary に別の ResourceDictionary を含めること(リソースの階層化)ができる。
これは外部リソースでも同様で、親リソースに含めたい子リソースを Source として参照すればよい。

ここでは以下のようなフォルダ構成を持つプロジェクトにおいて、「親スタイル」に「子スタイル」を統合する場合を考える。
画像
親リソース : General.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!-- Window --> <Style TargetType="Window"> <Setter Property="FontFamily" Value="Meiryo UI"/> <Setter Property="FontSize" Value="9"/> <Setter Property="SizeToContent" Value="WidthAndHeight"/> </Style> : (略) : <!-- 個別コントロールごとのスタイル定義をマージ --> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="_Control_Specified\DataGrid.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
子リソース : DataGrid.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!-- DataGrid : グリッド本体 --> <Style TargetType="DataGrid"> <Setter Property="Margin" Value="3"/> </Style> <!-- DataGridColumnHeader : 列ヘッダ--> <!-- フォント太字 : Bold --> <Style TargetType="DataGridColumnHeader" x:Key="Bold"> <Setter Property="FontWeight" Value="Bold"/> </Style> : (略) : </ResourceDictionary>
ウィンドウ : MainWindow.xaml
<Window x:Class="FileTreeMaker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"> <Window.Resources> <!-- 親リソース(General.xaml)をインポート --> <ResourceDictionary Source="_Assets\_Styles\General.xaml"/> </Window.Resources> <StackPanel Orientation="Vertical"> : (略) : <DataGrid> <DataGrid.Columns> <!-- スタイル「Bold」を適用(子リソースにて定義) --> <DataGridCheckBoxColumn Header="太字" HeaderStyle="{StaticResource ResourceKey=Bold}"/> </DataGrid.Columns> </DataGrid> </StackPanel> </Window>
解説
親リソースでは、ResourceDictionary 中に ResourceDictionary.MergedDictionaries を記述し、その中で子リソースを読み込んでいる。
これにより親リソースに子リソースが取り込まれるため、ウィンドウでは(子リソースが含まれた)親リソースのみを取り込めばよい。

注意が必要な点 : Window へのスタイル適用

Window に対して、スタイルを定義した外部リソースを適用する際は注意が必要となる。
( UserControl も同様。)

誤った方法

リソース : General.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!-- TargetType = "Window" でスタイルを定義 --> <Style TargetType="Window"> <Setter Property="FontFamily" Value="Yu Gothic"/> <Setter Property="SizeToContent" Value="WidthAndHeight"/> </Style> : (略) : </ResourceDictionary>
ウィンドウ : MainWindow.xaml
<Window x:Class="FileTreeMaker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"> <Window.Resources> <!-- リソース(General.xaml)をインポート --> <ResourceDictionary Source="_Assets\_Styles\General.xaml"/> </Window.Resources> : (略) : </Window>
Visual Studioのデザイナ表示 : MainWindow.xaml
画像
実際の表示(50%縮尺)
画像
解説
ここでは、Window に対して、外部リソースとして定義したスタイルを適用しようとしている。
Window.Resource としてファイル(General.xaml)を読み込んでおり、デザイナ上ではスタイルが正しく適用されているように見えるが、実際はスタイルが適用されていない。
( Window に対して定義したスタイルは適用されないが、子要素に対して定義したスタイルはこの方法でも適用される。)

正しい方法

リソース : General.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!-- TargetType に加え x:Key を指定 --> <Style TargetType="Window" x:Key="Window_General"> <Setter Property="FontFamily" Value="Yu Gothic"/> <Setter Property="SizeToContent" Value="WidthAndHeight"/> </Style> : (略) : </ResourceDictionary>
ウィンドウ : MainWindow.xaml
<Window x:Class="FileTreeMaker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Style="{DynamicResource ResourceKey=Window_General}"> <!-- ↑ キーを指定して DynamicResource で割り当て --> <Window.Resources> <!-- リソース(General.xaml)をインポート --> <ResourceDictionary Source="_Assets\_Styles\General.xaml"/> </Window.Resources> : (略) : </Window>
解説
Window.Resource としてファイル(General.xaml)を読み込む点は変わらないが、ここでは Window のスタイルに対してキー( x:Key )を追加している
参照側ではキーを指定して DynamicResource として割り当てることで、スタイルが正しく適用される。
( StaticResource として割り当てようとすると、プログラムが異常終了する。),

余談 : 「リソース」の呼称について

Windowsアプリケーションにおいてはアセンブリ中に画像などのバイナリファイルを埋め込むための機構が存在し、
これは同様に「リソース(アセンブリ・リソースまたはバイナリ・リソース)」と呼ばれ、WPFでも使用可能である。
当記事で解説する「リソース(オブジェクト・リソース)」との混同に注意すること。